package com.tn.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang.StringUtils;

import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.Query;
import com.tn.bo.Company;
import com.tn.bo.CreditMemo;
import com.tn.bo.Invoice;
import com.tn.bo.POHeader;
import com.tn.bo.Payment;
import com.tn.bo.XMLUtil;
import com.tn.service.OfyDataService;

public class InvoiceDAO {
	private static final Logger logger = Logger.getLogger(InvoiceDAO.class.getName());
	
	public Invoice getLatestInvoice(String invNum) {
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<Invoice> q = ofy.query(Invoice.class);
		q.filter("invoiceNumber", invNum);
		q.order("-invoiceVersion");
		Invoice r = null;
		for(Invoice p: q){
			 r = p;
			 break;
		}
		
		return r;
	}
	
	public List<Invoice> getInvoiceHistory(String invNum) {
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<Invoice> q = ofy.query(Invoice.class);
		q.filter("invoiceNumber", invNum);
		q.order("-invoiceVersion");
		List<Invoice> r = new ArrayList<Invoice>();
		for(Invoice p: q){
			p.setInvoiceXml(XMLUtil.getInvoiceXml(p));
			r.add(p);
		}
		return r;
	}
	
	public void updateInvoice(Invoice invoice){
		Objectify txn = OfyDataService.getInstance().beginOfyTransaction();
		txn.put(invoice);
		OfyDataService.getInstance().commitOfyTransaction(txn);
	}
	
	public Invoice insertUpdateInvoice(Invoice invoice){
		if(StringUtils.isEmpty(invoice.getUniqueInvoiceNumber())){
			invoice.setUniqueInvoiceNumber(generateInvoiceUniqueId("IV"));
		}
		
		if(StringUtils.isEmpty(invoice.getInvoiceNumber())){
			invoice.setInvoiceNumber(invoice.getUniqueInvoiceNumber());
		}
		
		if(StringUtils.isEmpty(invoice.getSupplierInvoiceNumber())){
			invoice.setSupplierInvoiceNumber(invoice.getUniqueInvoiceNumber());
		}
		invoice.setInvoiceVersion(1);
		Objectify txn = OfyDataService.getInstance().beginOfyTransaction();
		txn.put(invoice);
		
		try {
		Objectify txn2 = OfyDataService.getInstance().beginOfyTransaction();
		txn.put(invoice);
		String invoiceNumber = invoice.getInvoiceNumber();
		PurchaseOrderDAO poDAO = new PurchaseOrderDAO();
		POHeader po = poDAO.getLatestPOHeader(invoice.getPurchaseOrderNumber());
		ArrayList<String> poList = new ArrayList<String>();
		if(po.getOrderInvoices() != null ){
		 poList = po.getOrderInvoices();
		} 
		poList.add(invoiceNumber);
		po.setOrderInvoices(poList);
		poDAO.updatePOHeader(po);
		txn2.put(po);
		OfyDataService.getInstance().commitOfyTransaction(txn2);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
		}
		
		OfyDataService.getInstance().commitOfyTransaction(txn);
		return invoice;
	}
	
	
	public List<Invoice> fetchInvoiceSent(String companyId){
		//fetchAllInvoices();
		
		ArrayList<Invoice> retList = new ArrayList<Invoice>();
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<Invoice> q = ofy.query(Invoice.class).filter(
				"mainSupplierCompnay", companyId);
		
		filterInvoices(retList, q);
		
		return retList;
	}

	private void filterInvoices(ArrayList<Invoice> retList, Query<Invoice> q) {
		Map<String, List<Invoice>> invMap = new HashMap<String,List<Invoice>>();
		CompanyDAO cDao = new CompanyDAO();
		for(Invoice i : q){
			if(invMap.get(i.getInvoiceNumber()) == null){
				invMap.put(i.getInvoiceNumber(), new ArrayList<Invoice>());
			}
			
			if(invMap.get(i.getInvoiceNumber()).isEmpty()){
				invMap.get(i.getInvoiceNumber()).add(i);	
			}else{
				Invoice i2 = invMap.get(i.getInvoiceNumber()).get(0);
				if(i.getInvoiceVersion() > i2.getInvoiceVersion()){
					invMap.get(i.getInvoiceNumber()).add(0, i);
				}else{
					invMap.get(i.getInvoiceNumber()).add(i);
				}
			}
		}
		
		Set<Entry<String,List<Invoice>>> entrySet = invMap.entrySet();
		for(Entry<String, List<Invoice>> entry: entrySet){
			Invoice i = entry.getValue().get(0);
			Company c = cDao.findCompanyByCompanyId(i.getMainSupplierCompnay());
			if(c != null){
				i.setSupplierNameText(c.getCompanyName());
			}
			try{
			i.setInvoiceHistory(getInvoiceHistory(i.getInvoiceNumber()));
			i.setInvoiceXml(XMLUtil.getInvoiceXml(i));
			}catch(Exception ex){}
			retList.add(i);
		}
	}
	
	private String generateInvoiceUniqueId(String prefix){
		return prefix + System.currentTimeMillis();
	}


	public List<Invoice> fetchInvoicesReceived(String companyId) {
		
		ArrayList<Invoice> retList = new ArrayList<Invoice>();
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<Invoice> q = ofy.query(Invoice.class).filter(
				"invoicedToCompany", companyId);
		
		filterInvoices(retList, q);
		
		
		
		return retList;
	}


	public CreditMemo insertUpdateCreditMemoe(CreditMemo cmemo) {
		if(StringUtils.isEmpty(cmemo.getUniqueCreditMemoNumber())){
			cmemo.setUniqueCreditMemoNumber(generateInvoiceUniqueId("CM"));
		}
		
		if(StringUtils.isEmpty(cmemo.getCreditMemoNumber())){
			cmemo.setCreditMemoNumber(cmemo.getUniqueCreditMemoNumber());
		}
		
		if(StringUtils.isEmpty(cmemo.getSupplierCreditMemoNumber())){
			cmemo.setSupplierCreditMemoNumber(cmemo.getUniqueCreditMemoNumber());
		}

		Objectify txn = OfyDataService.getInstance().beginOfyTransaction();
		txn.put(cmemo);
		OfyDataService.getInstance().commitOfyTransaction(txn);
		return cmemo;
		
	}
	
	
	public Payment insertUpdatePayment(Payment payment){
		if(StringUtils.isEmpty(payment.getPaymentNumber())){
			payment.setUniquePaymentNumber(generateInvoiceUniqueId("PYT"));
			payment.setPaymentNumber(payment.getUniquePaymentNumber());
		}else if(StringUtils.isEmpty(payment.getUniquePaymentNumber())){
			payment.setUniquePaymentNumber(generateInvoiceUniqueId("PYT"));
		}
		
		Objectify txn = OfyDataService.getInstance().beginOfyTransaction();
		txn.put(payment);
		OfyDataService.getInstance().commitOfyTransaction(txn);
		return payment;
	}
	
	public List<Payment> fetchPaymentSent(String companyId){
		ArrayList<Payment> retList = new ArrayList<Payment>();
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<Payment> q = ofy.query(Payment.class).filter(
				"paymentOriginatorCompany", companyId);
		
		filterPayments(retList, q);
		
		return retList;
	}

	private void filterPayments(ArrayList<Payment> retList, Query<Payment> q) {
		Map<String, List<Payment>> pmtMap = new HashMap<String,List<Payment>>();
		
		for(Payment i : q){
			if(pmtMap.get(i.getPaymentNumber()) == null){
				pmtMap.put(i.getPaymentNumber(), new ArrayList<Payment>());
			}
			
			if(pmtMap.get(i.getPaymentNumber()).isEmpty()){
				pmtMap.get(i.getPaymentNumber()).add(i);	
			}else{
				Payment i2 = pmtMap.get(i.getPaymentNumber()).get(0);
				if(i.getPaymentVersion() > i2.getPaymentVersion()){
					pmtMap.get(i.getPaymentNumber()).add(0, i);
				}else{
					pmtMap.get(i.getPaymentNumber()).add(i);
				}
			}
		}
		
		Set<Entry<String,List<Payment>>> entrySet = pmtMap.entrySet();
		for(Entry<String, List<Payment>> entry: entrySet){
			Payment i = entry.getValue().get(0);
			try{
				i.setPaymentHistory(getPaymentHistory(i.getPaymentNumber()));
				i.setPaymentXml(XMLUtil.getPaymentXml(i));
				if(StringUtils.isNotEmpty(i.getEftAccountNumber())){
					i.setPaymentType("EFT");
				}else if(StringUtils.isNotEmpty(i.getCheckNumber())){
					i.setPaymentType("Check");
				}else {
					i.setPaymentType("Other");
				}
			}catch(Exception ex){}
			retList.add(i);
		}
	}
	
	public List<Payment> fetchPaymentReceived(String companyId){
		ArrayList<Payment> retList = new ArrayList<Payment>();
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<Payment> q = ofy.query(Payment.class).filter(
				"paymentRecipientMainCompany", companyId);
		
		filterPayments(retList, q);
		
		return retList;
	}


	public List<CreditMemo> fetchCreditMemoSent(String companyId) {
		//fetchAllCreditMemos();
		
		ArrayList<CreditMemo> retList = new ArrayList<CreditMemo>();
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<CreditMemo> q = ofy.query(CreditMemo.class).filter(
				"originatingCompany", companyId);
		
		filterCreditMemos(retList, q);
		
		return retList;
	}

	private void filterCreditMemos(ArrayList<CreditMemo> retList,
			Query<CreditMemo> q) {
		Map<String, List<CreditMemo>> cmoMap = new HashMap<String,List<CreditMemo>>();
		
		for(CreditMemo i : q){
			if(cmoMap.get(i.getCreditMemoNumber()) == null){
				cmoMap.put(i.getCreditMemoNumber(), new ArrayList<CreditMemo>());
			}
			
			if(cmoMap.get(i.getCreditMemoNumber()).isEmpty()){
				cmoMap.get(i.getCreditMemoNumber()).add(i);	
			}else{
				CreditMemo i2 = cmoMap.get(i.getCreditMemoNumber()).get(0);
				if(i.getCreditMemoVersion() > i2.getCreditMemoVersion()){
					cmoMap.get(i.getCreditMemoNumber()).add(0, i);
				}else{
					cmoMap.get(i.getCreditMemoNumber()).add(i);
				}
			}
		}
		
		Set<Entry<String,List<CreditMemo>>> entrySet = cmoMap.entrySet();
		
		for(Entry<String, List<CreditMemo>> entry: entrySet){
			CreditMemo i = entry.getValue().get(0);
			try{
				i.setCreditMemoHistory(getCreditMemoHistory(i.getCreditMemoNumber()));
				i.setCreditMemoXml(XMLUtil.getCreditMemoXml(i));
			}catch(Exception ex){}
			retList.add(i);
		}
	}


	public Object fetchCreditMemoReceived(String companyId) {
		ArrayList<CreditMemo> retList = new ArrayList<CreditMemo>();
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<CreditMemo> q = ofy.query(CreditMemo.class).filter(
				"creditMemoToCompany", companyId);
		
		filterCreditMemos(retList, q);
		
		return retList;
	}

	public Payment getLatestPayment(String pmtNum) {
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<Payment> q = ofy.query(Payment.class);
		q.filter("paymentNumber", pmtNum);
		q.order("-paymentVersion");
		Payment r = null;
		for(Payment p: q){
			 r = p;
			 break;
		}
		return r;
	}

	public void updatePayment(Payment payment) {
		Objectify txn = OfyDataService.getInstance().beginOfyTransaction();
		txn.put(payment);
		OfyDataService.getInstance().commitOfyTransaction(txn);
	}

	public List<Payment> getPaymentHistory(String pmtNum) {
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<Payment> q = ofy.query(Payment.class);
		q.filter("paymentNumber", pmtNum);
		q.order("-paymentVersion");
		List<Payment> r = new ArrayList<Payment>();
		for(Payment p: q){
			p.setPaymentXml(XMLUtil.getPaymentXml(p));
			r.add(p);
		}
		return r;
	}
	
	public CreditMemo getLatestCreditMemo(String cmNum) {
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<CreditMemo> q = ofy.query(CreditMemo.class);
		q.filter("creditMemoNumber", cmNum);
		q.order("-creditMemoVersion");
		CreditMemo r = null;
		for(CreditMemo p: q){
			 r = p;
			 break;
		}
		return r;
	}

	public void updateCreditMemo(CreditMemo creditMemo) {
		Objectify txn = OfyDataService.getInstance().beginOfyTransaction();
		txn.put(creditMemo);
		OfyDataService.getInstance().commitOfyTransaction(txn);
	}

	public List<CreditMemo> getCreditMemoHistory(String cmNum) {
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<CreditMemo> q = ofy.query(CreditMemo.class);
		q.filter("creditMemoNumber", cmNum);
		q.order("-creditMemoVersion");
		List<CreditMemo> r = new ArrayList<CreditMemo>();
		for(CreditMemo p: q){
			p.setCreditMemoXml(XMLUtil.getCreditMemoXml(p));
			r.add(p);
		}
		return r;
	}
	
	public void fetchAllCreditMemos(){
		Objectify ofy = OfyDataService.getInstance().getOfyService();
		Query<CreditMemo> query = ofy.query(CreditMemo.class);
		for(CreditMemo i: query){
			String msg = "CreditMemo BEFORE--> " + i;
			logger.severe(msg );
			if(i.getCreditMemoVersion() < 1 || StringUtils.isEmpty(i.getUniqueCreditMemoNumber())) {
				Objectify txn = OfyDataService.getInstance().beginOfyTransaction();
				i.setCreditMemoVersion(1);
				if(StringUtils.isEmpty(i.getUniqueCreditMemoNumber())){
					i.setUniqueCreditMemoNumber(i.getCreditMemoNumber());
				}
				txn.put(i);
				OfyDataService.getInstance().commitOfyTransaction(txn);
			}
			msg = "CreditMemo AFTER--> " + i;
			logger.severe(msg );
		}
		
		Objectify ofy2 = OfyDataService.getInstance().getOfyService();
		Query<Payment> query2 = ofy2.query(Payment.class);
		for(Payment i: query2){
			String msg = "Payment BEFORE--> " + i;
			logger.severe(msg );
			if(i.getPaymentVersion() < 1 || StringUtils.isEmpty(i.getUniquePaymentNumber())) {
				Objectify txn = OfyDataService.getInstance().beginOfyTransaction();
				i.setPaymentVersion(1);
				if(StringUtils.isEmpty(i.getUniquePaymentNumber())){
					i.setUniquePaymentNumber(i.getPaymentNumber());
				}
				txn.put(i);
				OfyDataService.getInstance().commitOfyTransaction(txn);
			}
			msg = "CreditMemo AFTER--> " + i;
			logger.severe(msg );
		}
	}

	public List<Invoice> searchInvoiceByFilters(Map<String, Object> filters) {
		ArrayList<Invoice> l = new ArrayList<Invoice>();
		try {
			Objectify ofy = OfyDataService.getInstance().getOfyService();
			Query<Invoice> q = ofy.query(Invoice.class);
			Set<Entry<String,Object>> entrySet = filters.entrySet();
			for(Entry<String,Object> entry: entrySet){
				q = q.filter(entry.getKey(), entry.getValue());
			}
			filterInvoices(l, q);
		} catch (Exception e) {
			logger.log(Level.SEVERE,"Error during Invoice Search.", e);
		}
		return l;
	}
	
	public List<CreditMemo> searchCreditMemoByFilters(Map<String, Object> filters) {
		ArrayList<CreditMemo> l = new ArrayList<CreditMemo>();
		try {
			Objectify ofy = OfyDataService.getInstance().getOfyService();
			Query<CreditMemo> q = ofy.query(CreditMemo.class);
			Set<Entry<String,Object>> entrySet = filters.entrySet();
			for(Entry<String,Object> entry: entrySet){
				q = q.filter(entry.getKey(), entry.getValue());
			}
			filterCreditMemos(l, q);
		} catch (Exception e) {
			logger.log(Level.SEVERE,"Error during Invoice Search.", e);
		}
		return l;
	}
	
	public List<Payment> searchPaymentByFilters(Map<String, Object> filters) {
		ArrayList<Payment> l = new ArrayList<Payment>();
		try {
			Objectify ofy = OfyDataService.getInstance().getOfyService();
			Query<Payment> q = ofy.query(Payment.class);
			Set<Entry<String,Object>> entrySet = filters.entrySet();
			for(Entry<String,Object> entry: entrySet){
				q = q.filter(entry.getKey(), entry.getValue());
			}
			filterPayments(l, q);
		} catch (Exception e) {
			logger.log(Level.SEVERE,"Error during Invoice Search.", e);
		}
		return l;
	}
}
